/* //device/system/broncho-ril/reference-ril.c
**
** Copyright 2006, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
**     http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/

#include <telephony/ril.h>
#include <stdio.h>
#include <assert.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>
#include <alloca.h>
#include "atchannel.h"
#include "at_tok.h"
#include <getopt.h>
#include "broncho-ril.h"
#include "ril_util.h"
#include "ril-sim.h"

#define LOG_TAG "RIL"
#include "broncho_log.h"

/**
 * Get the current card status.
 *
 * This must be freed using freeCardStatus.
 * @return: On success returns RIL_E_SUCCESS
 */
static int getCardStatus(RIL* thiz, RIL_CardStatus_v5 **pp_card_status) {
    static RIL_AppStatus app_status_array[] = {
        // SIM_ABSENT = 0
        { RIL_APPTYPE_UNKNOWN, RIL_APPSTATE_UNKNOWN, RIL_PERSOSUBSTATE_UNKNOWN,
          NULL, NULL, 0, RIL_PINSTATE_UNKNOWN, RIL_PINSTATE_UNKNOWN },
        // SIM_NOT_READY = 1
        { RIL_APPTYPE_SIM, RIL_APPSTATE_DETECTED, RIL_PERSOSUBSTATE_UNKNOWN,
          NULL, NULL, 0, RIL_PINSTATE_UNKNOWN, RIL_PINSTATE_UNKNOWN },
        // SIM_READY = 2
        { RIL_APPTYPE_SIM, RIL_APPSTATE_READY, RIL_PERSOSUBSTATE_READY,
          NULL, NULL, 0, RIL_PINSTATE_UNKNOWN, RIL_PINSTATE_UNKNOWN },
        // SIM_PIN = 3
        { RIL_APPTYPE_SIM, RIL_APPSTATE_PIN, RIL_PERSOSUBSTATE_UNKNOWN,
          NULL, NULL, 0, RIL_PINSTATE_ENABLED_NOT_VERIFIED, RIL_PINSTATE_UNKNOWN },
        // SIM_PUK = 4
        { RIL_APPTYPE_SIM, RIL_APPSTATE_PUK, RIL_PERSOSUBSTATE_UNKNOWN,
          NULL, NULL, 0, RIL_PINSTATE_ENABLED_BLOCKED, RIL_PINSTATE_UNKNOWN },
        // SIM_NETWORK_PERSONALIZATION = 5
        { RIL_APPTYPE_SIM, RIL_APPSTATE_SUBSCRIPTION_PERSO, RIL_PERSOSUBSTATE_SIM_NETWORK,
          NULL, NULL, 0, RIL_PINSTATE_ENABLED_NOT_VERIFIED, RIL_PINSTATE_UNKNOWN }
    };
    RIL_CardState card_state;
    int num_apps;

    int sim_status = RIL_getSIMStatus(thiz);
    if (sim_status == RIL_SIM_ABSENT) {
        card_state = RIL_CARDSTATE_ABSENT;
        num_apps = 0;
    } else {
        card_state = RIL_CARDSTATE_PRESENT;
        num_apps = 1;
    }

    // Allocate and initialize base card status.
    RIL_CardStatus_v5 *p_card_status = malloc(sizeof(RIL_CardStatus_v5));
    p_card_status->card_state = card_state;
    p_card_status->universal_pin_state = RIL_PINSTATE_UNKNOWN;
    p_card_status->gsm_umts_subscription_app_index = RIL_CARD_MAX_APPS;
    p_card_status->cdma_subscription_app_index = RIL_CARD_MAX_APPS;
    p_card_status->num_applications = num_apps;

    // Initialize application status
    int i;
    for (i = 0; i < RIL_CARD_MAX_APPS; i++) {
        p_card_status->applications[i] = app_status_array[RIL_SIM_ABSENT];
    }

    // Pickup the appropriate application status
    // that reflects sim_status for gsm.
    if (num_apps != 0) {
        // Only support one app, gsm
        p_card_status->num_applications = 1;
        p_card_status->gsm_umts_subscription_app_index = 0;

        // Get the correct app status
        p_card_status->applications[0] = app_status_array[sim_status];
    }

    *pp_card_status = p_card_status;
    return RIL_E_SUCCESS;
}

static void freeCardStatus(RIL_CardStatus_v5* pCardStatus)
{
	if(pCardStatus != NULL)
	{
		free(pCardStatus);
	}

	return;
}

static void requestGetSimState(RIL* thiz, void *data, size_t datalen, RIL_Token t)
{
	RIL_CardStatus_v5 *p_card_status = NULL;
	int result = 0;

	if ((result = getCardStatus(thiz, &p_card_status)) == RIL_E_SUCCESS)
	{
		RIL_onRequestComplete(thiz, t, RIL_E_SUCCESS, (char*)p_card_status, sizeof(RIL_CardStatus_v5));
	}
	else
	{
		RIL_onRequestComplete(thiz, t, result, NULL, 0);
	}
	freeCardStatus(p_card_status);

	return;
}

static void  requestSIM_IO(RIL* thiz, void *data, size_t datalen, RIL_Token t)
{
    ATResponse *p_response = NULL;
    RIL_SIM_IO_Response sr;
    int err;
    char *cmd = NULL;
    RIL_SIM_IO_v5 *p_args;
    char *line;

    memset(&sr, 0, sizeof(sr));

    p_args = (RIL_SIM_IO_v5 *)data;

    /* FIXME handle pin2 */

    if (p_args->data == NULL) {
        asprintf(&cmd, "AT+CRSM=%d,%d,%d,%d,%d",
                    p_args->command, p_args->fileid,
                    p_args->p1, p_args->p2, p_args->p3);
    } else {
        asprintf(&cmd, "AT+CRSM=%d,%d,%d,%d,%d,%s",
                    p_args->command, p_args->fileid,
                    p_args->p1, p_args->p2, p_args->p3, p_args->data);
    }

    err = ril_send_command_singleline(thiz, cmd, "+CRSM:", &p_response);

    if (err < 0 || p_response->success == 0) {
        goto error;
    }

    line = p_response->p_intermediates->line;

    err = at_tok_start(&line);
    if (err < 0) goto error;

    err = at_tok_nextint(&line, &(sr.sw1));
    if (err < 0) goto error;

    err = at_tok_nextint(&line, &(sr.sw2));
    if (err < 0) goto error;

    if (at_tok_hasmore(&line)) {
        err = at_tok_nextstr(&line, &(sr.simResponse));
        if (err < 0) goto error;
    }

    RIL_onRequestComplete(thiz, t, RIL_E_SUCCESS, &sr, sizeof(sr));
    at_response_free(p_response);
    free(cmd);

    return;
error:
    RIL_onRequestComplete(thiz, t, RIL_E_GENERIC_FAILURE, NULL, 0);
    at_response_free(p_response);
    free(cmd);
}

static void requestEnterNetworkDepersonalization(RIL* thiz, void*  data, size_t  datalen, RIL_Token  t)
{
	char cmd[64] = {0};
	const char* pin = ((const char**)data)[0];

	snprintf(cmd, 64, "AT+CLCK=\"PN\",0,%s", pin);
	if (ril_command_exec_for_result(thiz, cmd))
	{
		RIL_onRequestComplete(thiz, t, RIL_E_SUCCESS, NULL, 0);
	}
	else
	{
		RIL_onRequestComplete(thiz, t, RIL_E_PASSWORD_INCORRECT, NULL, 0);
	}

	return;
}

static void requestGetImsi(RIL* thiz, void*  data, size_t  datalen, RIL_Token  t)
{
	int err = 0;
	ATResponse* p_response = NULL;

    err = ril_send_command_numeric(thiz, "AT+CIMI", &p_response);
    if (err < 0 || p_response->success == 0) 
	{
        RIL_onRequestComplete(thiz, t, RIL_E_GENERIC_FAILURE, NULL, 0);
    } 
	else 
	{
        RIL_onRequestComplete(thiz, t, RIL_E_SUCCESS,
            p_response->p_intermediates->line, sizeof(char *));
    }
    at_response_free(p_response);

	return;
}

static void requestEnterSimPin2 (RIL* thiz, void*  data, size_t  datalen, RIL_Token  t)
{
	char	cmd[64] = {0};
	const char* pin2 = NULL;

	pin2 = ((const char**)data)[0];

	snprintf(cmd, 64, "AT+EPIN2=\"%s\"", pin2);
	if (ril_command_exec_for_result(thiz, cmd))
	{
		 RIL_onRequestComplete(thiz, t, RIL_E_SUCCESS, NULL, 0);
	}
	else
	{
		RIL_onRequestComplete(thiz, t, RIL_E_PASSWORD_INCORRECT, NULL, 0);
	}

	return;
}

static void requestChangeSimPin2(RIL* thiz, void*  data, size_t  datalen, RIL_Token  t)
{
	const char** pins = (const char**)data;
	char cmd[64] = {0};
	
	snprintf(cmd, 64, "AT+CPWD=\"P2\",\"%s\",\"%s\"", pins[0], pins[1]);
	if (ril_command_exec_for_result(thiz, cmd))
	{
		RIL_onRequestComplete(thiz, t, RIL_E_SUCCESS, NULL, 0);
	}
	else
	{
		RIL_onRequestComplete(thiz, t, RIL_E_PASSWORD_INCORRECT, NULL, 0);
	}

	return;
}

static void requestChangeSimPin(RIL* thiz, void*  data, size_t  datalen, RIL_Token  t)
{
	const char** pins = (const char**)data;
	char cmd[64] = {0};
	
	snprintf(cmd, 64, "AT+CPWD=\"SC\",\"%s\",\"%s\"", pins[0], pins[1]);
	if (ril_command_exec_for_result(thiz, cmd))
	{
		RIL_onRequestComplete(thiz, t, RIL_E_SUCCESS, NULL, 0);
	}
	else
	{
		RIL_onRequestComplete(thiz, t, RIL_E_PASSWORD_INCORRECT, NULL, 0);
	}

	return;
}

static void requestGetSimPinRemainTimes(RIL* thiz, void*  data, size_t  datalen, RIL_Token t)
{
	ATResponse* p_resp = NULL;

	if(ril_command_exec_singleline_for_result(thiz, "AT+EPINC?", "+EPINC:", &p_resp))
	{
		int count[4] = {0};
		
		at_line_scanf_int_int_int_int(p_resp->p_intermediates->line,
			&count[0], &count[1],  &count[2],  &count[3]);
		RIL_onRequestComplete(thiz, t, RIL_E_SUCCESS, &count, sizeof(count));
		at_response_free(p_resp);
	}
	else
	{
		RIL_onRequestComplete(thiz, t, RIL_E_GENERIC_FAILURE, NULL, 0);
	}

	return;
}

static void requestEnterSimPuk2(RIL* thiz, void*  data, size_t  datalen, RIL_Token  t)
{
	char	cmd[64] = {0};
	const char** pin2s = NULL;

	pin2s = (const char**)data;

	snprintf(cmd, 64, "AT+EPIN2=\"%s\",\"%s\"", pin2s[0], pin2s[1]);
	if (ril_command_exec_for_result(thiz, cmd))
	{
		 RIL_onRequestComplete(thiz, t, RIL_E_SUCCESS, NULL, 0);
	}
	else
	{
		RIL_onRequestComplete(thiz, t, RIL_E_PASSWORD_INCORRECT, NULL, 0);
	}

	return;
}

static void requestEnterSimPuk (RIL* thiz, void*  data, size_t  datalen, RIL_Token  t)
{
	char	cmd[64] = {0};
	const char** pins = NULL;

	pins = (const char**)data;

	snprintf(cmd, 64, "AT+CPIN=\"%s\",\"%s\"", pins[0], pins[1]);
	if (ril_command_exec_for_result(thiz, cmd))
	{
		 RIL_onRequestComplete(thiz, t, RIL_E_SUCCESS, NULL, 0);
	}
	else
	{
		RIL_onRequestComplete(thiz, t, RIL_E_PASSWORD_INCORRECT, NULL, 0);
	}

	return;
}

static void requestEnterSimPin (RIL* thiz, void*  data, size_t  datalen, RIL_Token  t)
{
	char	cmd[64] = {0};
	const char* pin = NULL;

	pin = ((const char**)data)[0];

	snprintf(cmd, 64, "AT+CPIN=\"%s\"", pin);
	if (ril_command_exec_for_result(thiz, cmd))
	{
		 RIL_onRequestComplete(thiz, t, RIL_E_SUCCESS, NULL, 0);
	}
	else
	{
		RIL_onRequestComplete(thiz, t, RIL_E_PASSWORD_INCORRECT, NULL, 0);
	}

	return;
}

int RIL_getSIMStatus (RIL* thiz)
{
    ATResponse *p_response = NULL;
    int err;
    int ret;
    char *cpinLine;
    char *cpinResult;
	int State = 0;

	State = RIL_GetState (thiz);
    if (State == RADIO_STATE_OFF || State == RADIO_STATE_UNAVAILABLE) {
        ret = RIL_SIM_NOT_READY;
        goto done;
    }

    err = ril_send_command_singleline(thiz, "AT+CPIN?", "+CPIN:", &p_response);

    if (err != 0) {
        ret = RIL_SIM_NOT_READY;
        goto done;
    }

    switch (at_get_cme_error(p_response)) {
        case CME_SUCCESS:
            break;

        case CME_SIM_NOT_INSERTED:
            ret = RIL_SIM_ABSENT;
            goto done;

        default:
            ret = RIL_SIM_NOT_READY;
            goto done;
    }

    /* CPIN? has succeeded, now look at the result */

    cpinLine = p_response->p_intermediates->line;
    err = at_tok_start (&cpinLine);

    if (err < 0) {
        ret = RIL_SIM_NOT_READY;
        goto done;
    }

    err = at_tok_nextstr(&cpinLine, &cpinResult);

    if (err < 0) {
        ret = RIL_SIM_NOT_READY;
        goto done;
    }

    if (0 == strcmp (cpinResult, "SIM PIN")) {
        ret = RIL_SIM_PIN;
        goto done;
    } else if (0 == strcmp (cpinResult, "SIM PUK")) {
		ATResponse* p_resp = NULL;
		
		ret = RIL_SIM_PUK;
		if(ril_command_exec_singleline_for_result(thiz, "AT+EPINC?", "+EPINC:", &p_resp))
		{
			int ignored = 0;
			int count = 0;
		
			at_line_scanf_int_int_int_int(p_resp->p_intermediates->line,
				&ignored, &ignored,  &count,  &ignored);
			at_response_free(p_resp);
			if(count == 0) 
			{
				ret = RIL_SIM_ABSENT;
			}
		}

        goto done;
    } else if (0 == strcmp (cpinResult, "PH-NET PIN")) {
        return RIL_SIM_NETWORK_PERSONALIZATION;
    } else if (0 != strcmp (cpinResult, "READY"))  {
        /* we're treating unsupported lock types as "sim absent" */
        ret = RIL_SIM_ABSENT;
        goto done;
    }

    at_response_free(p_response);
    p_response = NULL;
    cpinResult = NULL;

    ret = RIL_SIM_READY;

done:
    at_response_free(p_response);
    return ret;
}


int RIL_initialize_sim (RIL* thiz)
{
	return 1;
}

int RIL_onUnsolicited_sim (RIL* thiz, const char* s)
{
	int ret = 0;

	return ret;
}

int RIL_onRequest_sim (RIL* thiz, int request, void *data, size_t datalen, RIL_Token t)
{
	switch (request)
	{
	case RIL_REQUEST_GET_SIM_STATUS:
		requestGetSimState(thiz, data, datalen, t);
		break;

	case RIL_REQUEST_SIM_IO:
		requestSIM_IO(thiz, data,datalen,t);
		break;

	case RIL_REQUEST_ENTER_SIM_PIN:
		requestEnterSimPin(thiz, data, datalen, t);
		RIL_pollSIMState(thiz);
		break;

	case RIL_REQUEST_ENTER_SIM_PUK:
		requestEnterSimPuk(thiz, data, datalen, t);
		RIL_pollSIMState(thiz);
		break;

	case RIL_REQUEST_ENTER_SIM_PIN2:
		requestEnterSimPin2(thiz, data, datalen, t);
		break;

	case RIL_REQUEST_ENTER_SIM_PUK2:
		requestEnterSimPuk2(thiz, data, datalen, t);
		break;

	case RIL_REQUEST_GET_SIM_PIN_REMAIN_TIMES:
		requestGetSimPinRemainTimes(thiz, data, datalen, t);
		break;

	case RIL_REQUEST_CHANGE_SIM_PIN:
		requestChangeSimPin(thiz, data, datalen, t);
		RIL_pollSIMState(thiz);
		break;

	case RIL_REQUEST_CHANGE_SIM_PIN2:
		requestChangeSimPin2(thiz, data, datalen, t);
		break;

	case RIL_REQUEST_GET_IMSI:
		requestGetImsi(thiz, data, datalen, t);
		break;

	case RIL_REQUEST_ENTER_NETWORK_DEPERSONALIZATION:
		requestEnterNetworkDepersonalization(thiz, data, datalen, t);
		break;

	default:
		RIL_onRequestComplete(thiz, t, RIL_E_REQUEST_NOT_SUPPORTED, NULL, 0);
		break;

	}

	return 1;
}

